#ifndef simulador_h
#define simulador_h

#include <deque>
#include <vector>
#include <limits>
#include <iostream>
#include <sstream>
#include "fregues.h"
#include "exponencial.h"
#include "estatistica.h"
#include "es.h"
#include <cmath>
#include <cstdio>


#define int_conf(estimador_var, quantidade) (1.9600000000000 * (sqrt(estimador_var * 1.000000000) / sqrt(quantidade * 1.000000000)))

namespace SimuladorAD
{  
     /**
     * \brief A classe Simulador é a classe responsável pela execução da simulação propriamente dita.
     * Ela realiza a simulação de acordo com as configurações passadas, coletando e armazenando os 
     * resultados de cada rodada, apresentando o resultado da simulação.
     *
     */
    class Simulador {
      
	private:
	  
        Configuracao m_config;    /** Configuracao do simulador. */

		double m_proxima_chegada;	/**< Tempo de ocorrência da próxima chegada. */
		double m_termino_servico;	/**< Tempo de ocorrência do próximo término de serviço. */

		std::deque<Fregues> m_fila1; /**< Fila de espera 1. */
        std::deque<Fregues> m_fila2; /**< Fila de espera 2. */

		Exponencial* m_gerador_chegadas; /**< Gerador de tempos exponencialmente distribuidos para as chegadas ao sistema. */
		Exponencial* m_gerador_servico; /**< Gerador de tempos exponencialmente distribuidos para as durações dos serviços. */
		
		double m_tempo_atual; /**< Tempo de simulação. */

		int m_prox_id; /**< Id do próximo freguês. */

		Fregues m_fregues_em_servico; /**< Freguês atualmente em serviço. */
		
        int m_fregueses_servidos; /**<  Indica quantos fregueses já sairam do sistema na rodada atual.*/
        
		bool m_servidor_ocupado; /**< Indica se existe alguém recebendo serviço. */

		/**
		* Função para inicializar as variáveis do simulador.
		*/
		void inicializa(void);
		
		/**
		* Função que insere um freguês no servidor caso este se encontre vazio. 
		*/
		void coloca_em_servico(void);
		
		/**
		* Função que trata o evento término de serviço.
		* \param coleta Indica se os dados da rodada atual são para ser coletados.
		* \param rodada Número da rodada atual que esta sendo executada na simulação.
		*/
		void trata_termino_servico(bool coleta, int rodada);
		
		/**
		* Função que trata o evento chegada ao sistema.
		* \param rodada Número da rodada atual que esta sendo executada na simulação.
		*/
		void trata_chegada_no_sistema(int rodada);
	  

		/**
		* Função que executa a simulação no modo Batch.
		*/
        void rodar_batch(void);
        
		/**
		* Função que executa a simulação no modo Replicativo.
		*/
        void rodar_replicativo(void);
        
        /**
         * Executa uma rodada de tamanho 'tam_rodada'. \n
         * Os dados estatísticos dos fregueses atendidos podem ser coletados e armazenados ou não
         * (como por exemplo para ignorar os dados gerados na frase transiente).
         *
         * \param coleta Realiza a coleta de dados estatísticos ou não.
         * \param cor Indica a cor da rodada atual.
         * \param tam_rodada Número de fregueses que devem ser servidos durante essa rodada.
         */
        ResultadosSimulacao rodada(bool coleta, int cor, int tam_rodada);

        /** Escreve as estatísticas geradas até 'rodada'. \n
         *  Útil para gerar os gráficos para estimar a fase transiente.
         *  \param resultados Resultados acumulados que foram coletados até a presente rodada.
         * 	\param rodada Número da rodada atual que esta sendo executada na simulação.
         */
        void escrever_estatisticas(ResultadosSimulacao resultados, int rodada);


	/** Escreve os resultados finais da simulação: as médias, variâncias e seus respectivos intervalos de confiança.
	 *  \param resultados Resultados acumulados de toda a simulação que foram coletados.
	 */
        void escrever_resultados(ResultadosSimulacao res);


	public:
	
		ResultadosSimulacao resultados; /**< Armazena os dados coletados na rodada atual do simulador. */
        
        SaidaGrafico *saida_graf; /** Buffer de saída para gráficos. */
        SaidaPadrao *saida_std;	/** Buffer de saída para mensagens. */
	 
		
		/**
		* Construtor da classe Simulador.
		*
		* \param conf Configurações iniciais do simulador.
		*
		*/
        Simulador(Configuracao conf);

		/**
		* Executa o simulador de acordo com as configurações iniciais passadas.
		*/
		void executa();


        /** Gera a saída das estatísticas em formato apropriado para geração dos gráficos
         * 
         * \param est_media	Estimador da média.
         * \param est_var Estimador da variância.
         * \param int_conf Intervalo de confiança gerado. 
         */
        void gerar_saida(double& est_media, double& est_var, double& int_conf) const;

		/**
		* Volta o simulador para seu estado inicial, removendo todos os fregueses e os dados coletados,
		* mas mantem o estado dos PRNGs.
		*/
		void reinicia_simulador();

		/**
		* Retorna a semente utilizada no gerador aleatorio.
		*/
		long int semente_gerador_chegadas(void);

		/**
		* Retorna a semente utilizada no gerador aleatorio.
		*/
		long int semente_gerador_servico(void);
		
		/**
         * Coloca os geradores de chegadas para operar em modo determinístico.
         *
         * \param ativado TRUE para ativar o modo deterministico nos geradores de chegadas, FALSE para desativar.
         */
        void geradores_deterministicos(bool ativado);
		
		/**
         * Coloca o simulador para operar em modo verboso, ou seja, explicitando cada evento que está ocorrendo.
         *
         * \param ativado TRUE para ativar, FALSE para desativar.
         */
        void verboso(bool ativado);

    };    
}

#endif //simulador.h
